#include "httpresponse.h"


const std::unordered_map<int, std::string> HttpResponse::CODE_STATUS = {
    {200, "OK"},
    {400, "Bad Request"},
    {403, "Forbidden"},
    {404, "Not Found"},
};

const std::unordered_map<int, std::string> HttpResponse::ERROR_CODE_PATH = {
    {400, "/400.html"},
    {403, "/403.html"},
    {404, "/404.html"},
};

HttpResponse::HttpResponse()
{
    code_ = -1;
    isKeepAlive_ = false;
    path_ = "";
    srcDir_ = "";
    mmFile_ = nullptr;
    mmFileStat_ = {0};
}

HttpResponse::~HttpResponse()
{
}

void HttpResponse::Init(const std::string &srcDir, std::string path, bool isKeepAlive, int code)
{
    assert(srcDir != "");
    if (mmFile_)
    {
        UnmapFile();
    }
    code_ = code;
    isKeepAlive_ = isKeepAlive;
    path_ = path;
    srcDir_ = srcDir;
    mmFile_ = nullptr;
    mmFileStat_ = {0};
}

void HttpResponse::MakeResponse(Buffer& buffer)
{
    // 判断请求的资源文件
    if(stat((srcDir_+path_).data(), &mmFileStat_) < 0 || S_ISDIR(mmFileStat_.st_mode)) {
        code_ = 404;
    } else if(!(mmFileStat_.st_mode & S_IROTH)) {
        code_ = 403;
    } else if(code_ = -1) {
        code_ = 200;
    }
    // 填充响应
    ResetCode();
    AddStateLine(buffer);
    AddHeader(buffer);
    AddContent(buffer);
}

void HttpResponse::UnmapFile()
{
    if (mmFile_)
    {
        munmap(mmFile_, mmFileStat_.st_size);
        mmFile_ = nullptr;
    }
}

char *HttpResponse::File()
{
    return mmFile_;
}

size_t HttpResponse::FileLen()
{
    return mmFileStat_.st_size;
}

void HttpResponse::AddStateLine(Buffer &buffer)
{
    std::string state;
    if (CODE_STATUS.count(code_) == 1)
    {
        state = CODE_STATUS.find(code_)->second;
    } else {
        code_ = 400;
        state = CODE_STATUS.find(code_)->second;
    }

    buffer.Append("HTTP/1.1 " + std::to_string(code_) + " " + state + "\r\n");
}

void HttpResponse::AddHeader(Buffer &buffer)
{
    buffer.Append("Connection: ");
    if(isKeepAlive_) {
        buffer.Append("keep-alive\r\n");
        buffer.Append("keep-alive: max=6, timeout=120\r\n");
    } else {
        buffer.Append("close\r\n");
    }
    buffer.Append("Content-type: " + GetFileType() + "\r\n");
}

void HttpResponse::AddContent(Buffer &buffer)
{
    //打开文件并进行内存映射
    int srcFd = open((srcDir_+path_).data(), O_RDONLY);
    if(srcFd < 0) {
        ErrorContent(buffer, "File NotFound!");
        return;
    }
    int* mmRes = (int*)mmap(0, mmFileStat_.st_size, PROT_READ, MAP_PRIVATE, srcFd, 0);
    if(*mmRes == -1) {
        ErrorContent(buffer, "File NotFound!");
        return;
    }
    mmFile_ = (char*)mmRes;
    close(srcFd);
    buffer.Append("Content-length: " + std::to_string(mmFileStat_.st_size) + "\r\n\r\n");
}

std::string HttpResponse::GetFileType()
{
    return std::string();
}

void HttpResponse::ErrorContent(Buffer &buffer, std::string msg)
{
    std::string body;
    std::string status;
    body += "<html><title>Error</title>";
    body += "<body bgcolor=\"ffffff\">";
    if(CODE_STATUS.count(code_) == 1) {
        status = CODE_STATUS.find(code_)->second;
    } else {
        status = "Bad Request";
    }
    body += std::to_string(code_) + " : " + status  + "\n";
    body += "<p>" + msg + "</p>";
    body += "<hr><em>TinyWebServer</em></body></html>";

    buffer.Append("Content-length: " + std::to_string(body.size()) + "\r\n\r\n");
    buffer.Append(body);
}

void HttpResponse::ResetCode()
{
    if(ERROR_CODE_PATH.count(code_) == 1) {
        path_ = ERROR_CODE_PATH.find(code_)->second;
        stat((srcDir_+path_).data(), &mmFileStat_);
    }
}
